import { Dictionary } from './interface';

const { hasOwnProperty } = Object.prototype;

// eslint-disable-next-line @typescript-eslint/no-empty-function
export function noop(): void {}

export async function sleep(timeoutsMs: number): Promise<void> {
	return new Promise((resolve) => {
		setTimeout(() => {
			resolve();
		}, timeoutsMs);
	});
}

export function isNumeric(val: string | number): val is string {
	return typeof val === 'number' || /^\d+(\.\d+)?$/.test(val);
}

export function isObject(val: unknown): val is Dictionary {
	return val !== null && typeof val === 'object';
}

export function isDate(val: any): val is Date {
	return (
		Object.prototype.toString.call(val) === '[object Date]' &&
		!isNaN(val.getTime())
	);
}

/**
 * Replace with template.
 *   `I'm ${name}` + { name: 'bamboo' } = I'm bamboo
 */
export function replaceMessage(
	template: string,
	kv: Record<string, string>,
): string {
	return template.replace(/\$\{\w+\}/g, (str: string) => {
		const key = str.slice(2, -1);
		return kv[key];
	});
}

export function devWarning(valid: boolean, component: string, message: string) {
	if (process.env.NODE_ENV !== 'production' && !valid && console != null) {
		console.error(`Warning: [fnx ${component}] ${message}`);
	}
}

function assignKey(to: Dictionary, from: Dictionary, key: string) {
	const val = from[key];

	if (!hasOwnProperty.call(to, key) || !isObject(val)) {
		to[key] = val;
	} else {
		to[key] = deepAssign(Object(to[key]), from[key]);
	}
}

export function deepAssign(to: Dictionary, from: Dictionary): Dictionary {
	Object.keys(from).forEach((key) => {
		assignKey(to, from, key);
	});

	return to;
}

export function deepClone(obj: Dictionary): Dictionary {
	if (Array.isArray(obj)) {
		return obj.map((item) => deepClone(item));
	}

	if (typeof obj === 'object') {
		return deepAssign({}, obj);
	}

	return obj;
}
